﻿// Program.cs is part of AGatherBot.
//
// AGatherBot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as
// published by the Free Software Foundation.
//
// AGatherBot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public version 2 License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGatherBot. If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Reflection;

namespace AGatherBot
{
    // TODO: Document
    // TODO: WriteLine, WriteError and ReadLine methods
    public static class Program
    {
        // Constant Members.
        public static readonly char[] ForbiddenCommandChars = new Char[] { ' ' };

        public static SortedDictionary<string, ISharpCommand> Commands { get; private set; }

        private static AGatherManager GatherManager { get; set; }

        #region Commands

        public static void RegisterCommand(ISharpCommand command)
        {
            if (Commands.ContainsKey(command.CommandName))
            {
                throw new ArgumentException("Command already exists.");
            }

            if (command.CommandName.IndexOfAny(ForbiddenCommandChars) != -1)
            {
                throw new ArgumentException("Invalid command name.");
            }

            lock (Commands)
            {
                Commands.Add(command.CommandName, command);
            }
        }

        /// <summary>
        /// Update the commands in the command collection.
        /// </summary>
        private static void UpdateCommands()
        {
            UpdateCommand(new AGatherBot.Commands.ExitCommand());
            UpdateCommand(new AGatherBot.Commands.HelpCommand());
            UpdateCommand(new AGatherBot.Commands.ExecFileCommand());
        }

        private static void UpdateCommand(ISharpCommand cmd)
        {
            if (Commands.ContainsKey(cmd.CommandName))
            {
                Commands.Remove(cmd.CommandName);
            }

            RegisterCommand(cmd);
        }

        #endregion

        #region Main

        static void Main(string[] args)
        {
            Console.WindowWidth                         = Console.LargestWindowWidth;
            AppDomain.CurrentDomain.UnhandledException  += UnhandledException;

            Console.WriteLine("Welcome to AGatherBot Testing Zone.");
            Console.WriteLine("Current version of ASharpConsole: {0}", Assembly.GetExecutingAssembly().GetName().Version);
            Console.WriteLine("--------------------------------------");
            Console.WriteLine();

            Console.WriteLine("Creating AGatherManager instance...");

            Commands = new SortedDictionary<string, ISharpCommand>(StringComparer.OrdinalIgnoreCase);

            // An aggregate catalog that combines multiple catalogs
            var catalog = new AggregateCatalog();
            // Adds all the parts found in the same assembly as the Program class
            catalog.Catalogs.Add(new DirectoryCatalog("D:\\Development\\AGatherBot\\debug"));

            // Create the CompositionContainer with the parts in the catalog
            var _container = new CompositionContainer(catalog);

            GatherManager = new AGatherManager();

            // Fill the imports of this object
            try
            {
                _container.ComposeParts(GatherManager);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }

            Console.WriteLine("AGatherManager instance created.");

            // If arguments exist, validate and handle them.
            if (args.GetLength(0) == 1)
            {
                // If the given file doesn't exist, display an error.
                if (!File.Exists(args[0]))
                {
                    Console.Error.WriteLine("*** Error: File wasn't found.");
                }
                // If the file exists, execute the commands.
                else
                {
                    // TODO: Who executes on console?
                    Commands["ExecFile"].Execute(Properties.Resources.Console, "@ " + args[0]);
                }
            }

            Console.WriteLine("Starting to listen to user input.");
            Console.WriteLine("For any help, please type: \"Help\"");
            Console.WriteLine("--------------------------------------");
            Console.WriteLine();

            UpdateCommands();
            ReadCommands();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Catches unhandled exceptions and reports them accordingly.
        /// </summary>
        private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var exp = e.ExceptionObject as Exception;

            if (exp == null)
            {
                Console.Error.WriteLine("*** ERROR: An unhandled exception occured!");
            }
            else
            {
                Console.Error.WriteLine("*** ERROR: (Unhandled) " + exp.Message);

#if DEBUG
                Console.Error.WriteLine(exp.ToString());
#endif
            }
        }

        /// <summary>
        /// Executes user input based on the known commands.
        /// </summary>
        /// <param name="input">The user's input.</param>
        public static void ExecuteInput(string input)
        {
            // If the given input is nothing, do nothing.
            if (string.IsNullOrWhiteSpace(input))
            {
                return;
            }

            input = input.Trim();

            // Extract the command name.
            var command = input.Split()[0];

            // If the command was not found, write a suitable error.
            if (!Commands.ContainsKey(command))
            {
                Console.Error.WriteLine("*** Command not found: {0}", command);
            }
            // Else, if command was found, execute it whilst giving it the input as a parameter.
            else
            {
                Console.WriteLine(Commands[command].Execute(Properties.Resources.Console, input));
            }
        }

        /// <summary>
        /// Infinite loop to read commands, parse them and execute them.
        /// </summary>
        public static void ReadCommands()
        {
            while (true)
            {
                // Try to parse the input.
                try
                {
                    // TODO: Debug only?
#if DEBUG
                    Console.Write("$ ");
#endif

                    // Get the user's input.
                    var input = Console.ReadLine();

                    ExecuteInput(input);
                }
                // If any unknown error was throwen, print a suitable error.
                catch (Exception expError)
                {
                    Console.Error.WriteLine("*** ERROR: " + expError.Message);

#if DEBUG
                    Console.Error.WriteLine(expError.ToString());
#endif
                }
            }
        }

        #endregion
    }
}
